package com.mxpio.erp.inventory.service.impl;

import com.mxpio.erp.common.LotStatusUtil;
import com.mxpio.erp.common.enums.InspectionEnums;
import com.mxpio.erp.common.enums.InventoryEnums;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums.BpmnStatus;
import com.mxpio.erp.common.enums.OrderStatusEnums.CloseStatus;
import com.mxpio.erp.common.inventory.entity.*;
import com.mxpio.erp.common.inventory.service.BorrowApplyLineService;
import com.mxpio.erp.common.inventory.service.BorrowApplyService;
import com.mxpio.erp.common.inventory.service.LotService;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.common.vo.OrderExcuteVo;
import com.mxpioframework.common.exception.MBootException;
import com.mxpioframework.common.util.BeanReflectionUtils;
import com.mxpioframework.common.vo.Result;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Operator;
import com.mxpioframework.security.util.SecurityUtils;
import com.mxpioframework.system.service.impl.BaseServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class BorrowApplyServiceImpl extends BaseServiceImpl<BorrowApply> implements BorrowApplyService {
	@Autowired
	private BorrowApplyLineService borrowApplyLineService;

	@Autowired
	private LotService lotService;

	@Override
	@Transactional(readOnly = false)
	public Result<BorrowApply> updateCloseStatus(String bizNo, CloseStatus closeStatus) {
		BorrowApply wo = this.getById(BorrowApply.class,bizNo);
		if(wo==null) {
			return Result.error("申请单号未能查询到数据，请确认");
		}
		if(CloseStatus.CLOSED.equals(closeStatus) && wo.getCloseStatus().equals(CloseStatus.CLOSED.getStatusCode())){
			return Result.error("非打开状态不可关闭");
		}else if(CloseStatus.OPEN.equals(closeStatus) && wo.getCloseStatus().equals(CloseStatus.OPEN.getStatusCode())){
			return Result.error("非关闭状态不可打开");
		}
		wo.setCloseStatus(closeStatus.getStatusCode());
        this.update(wo);
        return Result.OK(wo);
	}
	
	@Override
	@Transactional(readOnly = false)
	public Result<BorrowApply> updateBpmnStatus(String bizNo, BpmnStatus bpmnStatus) {
		BorrowApply wo = this.getById(BorrowApply.class,bizNo);
		if(wo==null) {
			return Result.error("申请单号未能查询到数据，请确认");
		}
		if(bpmnStatus.getStatusCode().equals(BpmnStatus.AUDITING.getStatusCode())&&!wo.getBpmnStatus().equals(BpmnStatus.CREATE.getStatusCode())){
    		return Result.error("非开立状态不可提交");
		}
		if(bpmnStatus.getStatusCode().equals(BpmnStatus.AUDITED.getStatusCode())&&!wo.getBpmnStatus().equals(BpmnStatus.AUDITING.getStatusCode())){
    		return Result.error("非审核中状态不可审核");
		}
		if(bpmnStatus.getStatusCode().equals(BpmnStatus.CREATE.getStatusCode())&&
				!(wo.getBpmnStatus().equals(BpmnStatus.AUDITING.getStatusCode())||wo.getBpmnStatus().equals(BpmnStatus.AUDITED.getStatusCode()))){
    		return Result.error("非审核中、已审核状态不可弃审");
		}
		wo.setBpmnStatus(bpmnStatus.getStatusCode());
		wo.setReviewer(SecurityUtils.getLoginUsername());
		wo.setReviewTime(new Date());
		wo.setReturnStatus(InventoryEnums.ReturnStatus.APPLY.getTypeCode());//更新归还状态
        this.update(wo);
        return Result.OK(wo);
	}

	@Override
	@Transactional(readOnly = false)
	public Result<BorrowApply> execute(String bizNo,String applyType, OrderExcuteVo orderExcuteVo) {
		BorrowApply wo = this.getById(BorrowApply.class,bizNo);
		if(wo==null) {
			throw new MBootException("申请单号未能查询到数据，请确认");
		}
		if(wo.getCloseStatus().equals(CloseStatus.CLOSED.getStatusCode())) {
			throw new MBootException("已关闭状态不可执行");
		}
		List<OrderExcuteLineVo> lines = orderExcuteVo.getLines();//

		if(lines==null||lines.size()==0) {
			throw new MBootException("申请单物资明细无执行数据，无法执行");
		}

		if("0".equals(applyType)) {//借出
			if(!wo.getBpmnStatus().equals(BpmnStatus.AUDITED.getStatusCode())){
				throw new MBootException("非已审核状态不可执行认");
			}

			if(StringUtils.equals(wo.getOrderStatus(), OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())) {
				throw new MBootException("申请单已执行，不能重复执行");
			}
			//借用
			Map<String, BigDecimal> sumUseItemQuantityMap = new HashMap<String, BigDecimal>();//汇总明细行中相同物料情况
			Map<String,List<VInvLotStock>> stockListMap = new HashMap<>();
			List<InvFlow> insertFlowList = new ArrayList<>();
			Map<String,BigDecimal> usedStockMap = new HashMap<>();//批次已占用库存
			List<InvFlow> invFlows = new ArrayList<>();

			for(OrderExcuteLineVo vo:lines) {
				//1 查询库存视图
				List<VInvStock> stocks = JpaUtil.linq(VInvStock.class)
						.equal("itemCode", vo.getItemCode())
						.equal("whCode", vo.getWhCode()).list();
				if(stocks.size()==0) {
					throw new MBootException("申请借用的物资"+vo.getItemCode()+"【"+vo.getLotNo()+"】没有对应库存记录");
				}

				BigDecimal stockAvailable = stocks.get(0).getQuantity();
				BigDecimal useQuantitySum = sumUseItemQuantityMap.get(vo.getItemCode());//该循环中，该物料对应行已经占用数量和
				if (useQuantitySum==null){
					sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity());
					if(stockAvailable.compareTo(vo.getQuantity()) < 0) {
						throw new MBootException("申请借用的物资已借用数量已不满足借用条件(不大于库存)");
					}
				}else{
					if(stockAvailable.compareTo(vo.getQuantity().add(useQuantitySum)) < 0) {
						throw new MBootException("申请借用的物资已借用数量已不满足借用条件(不大于库存)");
					}
					sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity().add(useQuantitySum));//累计数量
				}
				//自动分配批次
				//开始匹配库存
				List<VInvLotStock> itemLotStockList = null;
				if (stockListMap.containsKey(vo.getItemCode())){
					itemLotStockList = stockListMap.get(vo.getItemCode());
				}else{
					//根据先进先出，查询批次
					Criteria lotStockCriteria = Criteria.create().addCriterion("whCode", Operator.EQ, wo.getWhCode()).
							addCriterion("itemCode",Operator.EQ, vo.getItemCode()).
							addCriterion("lotStatus",Operator.EQ, LotStatusUtil.toBinaryString("0"));//增加批次状态过滤
					lotStockCriteria.addOrder(new com.mxpioframework.jpa.query.Order("createTime",false));//根据创建时间，先进(批次号小)先出。
					itemLotStockList = JpaUtil.linq(VInvLotStock.class).gt("quantity",0).where(lotStockCriteria).list();//过滤调批次为零的
					stockListMap.put(vo.getItemCode(),itemLotStockList);
				}
				BigDecimal quantity = vo.getQuantity();//vo行待执行数据
				for (VInvLotStock stock:itemLotStockList){
					BigDecimal stockQuantity = stock.getQuantity();//该批次库存数量
					if (usedStockMap.containsKey(stock.getLotNo())){
						BigDecimal used = usedStockMap.get(stock.getLotNo());
						stockQuantity = stockQuantity.subtract(used);
					}
					if (stockQuantity.compareTo(BigDecimal.ZERO)<=0){//库存小于等于0，直接跳过
						continue;
					}
					if (quantity.compareTo(stockQuantity)>0){//大于该批次库存数量
						InvFlow invFlow = new InvFlow();
						invFlow.setAccessType(OrderEnums.AccessType.OUT.getCode());//出
						invFlow.setItemCode(vo.getItemCode());
						invFlow.setWhCode(wo.getWhCode());
						//取批次库存视图中的库位信息
						//invFlow.setRegionCode(stock.getRegionCode());
						//invFlow.setAreaCode(stock.getAreaCode());
						//invFlow.setLocationCode(stock.getLocationCode());

						invFlow.setQuantity(stockQuantity);//取库存批次数量
						invFlow.setMemo(wo.getMemo());
						invFlow.setOriginBizNo(wo.getBizNo());
						invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
						invFlow.setLotNo(stock.getLotNo());
						invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
						quantity = quantity.subtract(stockQuantity);//
						insertFlowList.add(invFlow);
						if (usedStockMap.containsKey(invFlow.getLotNo())){
							BigDecimal used = usedStockMap.get(invFlow.getLotNo());
							usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
						}else{
							usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
						}
					}else{//小于等于批次库存的情况
						InvFlow invFlow = new InvFlow();
						invFlow.setAccessType(OrderEnums.AccessType.OUT.getCode());
						invFlow.setItemCode(vo.getItemCode());
						invFlow.setWhCode(wo.getWhCode());
						//取批次库存视图中的库位信息
						//invFlow.setRegionCode(stock.getRegionCode());
						//invFlow.setAreaCode(stock.getAreaCode());
						//invFlow.setLocationCode(stock.getLocationCode());

						invFlow.setQuantity(quantity);
						invFlow.setMemo(wo.getMemo());
						invFlow.setOriginBizNo(wo.getBizNo());
						invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
						invFlow.setLotNo(stock.getLotNo());
						invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
						insertFlowList.add(invFlow);
						if (usedStockMap.containsKey(invFlow.getLotNo())){
							BigDecimal used = usedStockMap.get(invFlow.getLotNo());
							usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
						}else{
							usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
						}
						break;//跳出
					}
				}
			}
			if (insertFlowList.size()>0){
				for (InvFlow invFlow:insertFlowList){
					InvFlow splitInFlow = new InvFlow();
					BeanReflectionUtils.copyProperties(splitInFlow,invFlow);
					Lot splitNewLot = lotService.splitNewLot(invFlow.getLotNo(), invFlow.getSubLotIndex(), InspectionEnums.LotStatusBitIndex.IB13.getIndex());
					splitInFlow.setAccessType(OrderEnums.AccessType.IN.getCode());
					splitInFlow.setLotNo(splitNewLot.getLotNo());
					splitInFlow.setSubLotIndex(splitNewLot.getSubLotIndex());
					invFlows.add(splitInFlow);
				}
				insertFlowList.addAll(invFlows);//合并
				JpaUtil.save(insertFlowList);//保存
			}

			wo.setOrderStatus(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());
			wo.setExecutor(SecurityUtils.getLoginUsername());
			wo.setExecuteTime(new Date());
			wo.setReturnStatus(InventoryEnums.ReturnStatus.BORROWED.getTypeCode());//更新借出状态为已借出
		}else {
			List<InvFlow> invFlows = new ArrayList<>();//所有的
			for(OrderExcuteLineVo vo:lines) {
				Map<String, BigDecimal> sumQuantity = new HashMap<String, BigDecimal>();
				// 已归还的批次数量
				List<InvFlow> excutedReturnList = JpaUtil.linq(InvFlow.class)
						.equal("originBizNo", vo.getBizOrderNo())
						.equal("originBizLineNo", vo.getBizOrderlineNo())
						.equal("lotNo", vo.getLotNo())
						.equal("accessType", OrderEnums.AccessType.OUT.getCode())
						.equal("subLotIndex", vo.getSubLotIndex())
						.list();
				BigDecimal completeQuantity = BigDecimal.ZERO;
				if (excutedReturnList.size()>0){
					for (InvFlow invFlow:excutedReturnList){
						completeQuantity = completeQuantity.add(invFlow.getQuantity());
					}
				}
				BigDecimal currentQuan = sumQuantity.get(vo.getBizOrderNo() + "#" + vo.getBizOrderlineNo());//剩余最大可执行数量
				if(currentQuan == null) {
					BigDecimal maxQuantity = vo.getLineQuantity()==null?BigDecimal.ZERO:vo.getLineQuantity();
					currentQuan = maxQuantity.subtract(completeQuantity).subtract(vo.getQuantity());
					sumQuantity.put(vo.getBizOrderNo() + "#" + vo.getBizOrderlineNo(),currentQuan);//处理拆批情况
				}else {
					currentQuan = currentQuan.subtract(vo.getQuantity());
				}
				if(currentQuan.compareTo(BigDecimal.ZERO) < 0) {
					throw new MBootException("申请归还的物资"+ vo.getItemCode() + "超过最大执行数量");
				}

				//锁定批次库存的出
				InvFlow splitOut = new InvFlow();
				splitOut.setAccessType(OrderEnums.AccessType.OUT.getCode());
				splitOut.setItemCode(vo.getItemCode());
				splitOut.setWhCode(wo.getWhCode());
				//库位信息
				//splitOut.setRegionCode(vo.getRegionCode());
				//splitOut.setAreaCode(vo.getAreaCode());
				//splitOut.setLocationCode(vo.getLocationCode());
				splitOut.setLotNo(vo.getLotNo());
				splitOut.setSubLotIndex(vo.getSubLotIndex());
				splitOut.setQuantity(vo.getQuantity());

				splitOut.setMemo(wo.getMemo());
				splitOut.setOriginBizNo(wo.getBizNo());
				splitOut.setOriginBizLineNo(vo.getBizOrderlineNo());
				invFlows.add(splitOut);
				//合并至源批次
				Lot currentLot = JpaUtil.linq(Lot.class).equal("lotNo", splitOut.getLotNo()).equal("subLotIndex", splitOut.getSubLotIndex()).findOne();//
				if (currentLot==null){
					throw new MBootException("获取批次信息有误");
				}
				if (StringUtils.isBlank(currentLot.getOriginSubLotIndex())){
					throw new MBootException("批次【"+currentLot.getLotNo()+"】未记录源批次索引信息！");
				}
				//源批
				Lot originLot = JpaUtil.linq(Lot.class).equal("lotNo", splitOut.getLotNo()).equal("subLotIndex", currentLot.getOriginSubLotIndex()).findOne();
				if (originLot==null){
					throw new MBootException("获取源批次信息有误");
				}
				InvFlow originIn = new InvFlow();
				BeanReflectionUtils.copyProperties(originIn,splitOut);
				originIn.setAccessType(OrderEnums.AccessType.IN.getCode());
				//库位信息
				originIn.setLotNo(originLot.getLotNo());
				originIn.setSubLotIndex(originLot.getSubLotIndex());
				invFlows.add(originIn);
			}

			JpaUtil.save(invFlows);//保存流水
			if (wo.getReturnStatus().equals(InventoryEnums.ReturnStatus.BORROWED.getTypeCode())){
				wo.setReturnStatus(InventoryEnums.ReturnStatus.RETURNING.getTypeCode());
			}
		}
		if("1".equals(applyType)) {//如果是归还，最后查询整单的归还操作情况
			Criteria c = Criteria.create().addCriterion("bizNo", Operator.EQ, bizNo);
			List<BorrowApplyLine> applyLineList = borrowApplyLineService.list(BorrowApplyLine.class, c);
			if (applyLineList.size()>0){
				borrowApplyLineService.handleBorrowApplyLinesReturnedQuantity(applyLineList);//关联已归还数量
				if (checkBorrowApplyReturnedClear(applyLineList)){//如果归还完毕，更新借用状态字段
					wo.setReturnStatus(InventoryEnums.ReturnStatus.CLEAR.getTypeCode());//已还清
				}
			}
		}
		this.update(wo);
		return Result.OK(wo);
	}
	private Boolean checkBorrowApplyReturnedClear(List<BorrowApplyLine> applyLineList){
		if (applyLineList==null||applyLineList.size()==0){
			return false;
		}
		for (BorrowApplyLine line :applyLineList){
			if (line.getQuantity().compareTo(line.getActualQuantity())!=0){
				return false;
			}
		}
		return true;
	}
}
